/**
 *    Copyright (C) 2009 Victor Smirnov <aist11@gmail.com>
 *
 *    This program is free software; you can redistribute it and/or modify
 *    it under the terms of the GNU General Public License as published by
 *    the Free Software Foundation; either version 3 of the License, or
 *    (at your option) any later version.
 *
 *    This program is distributed in the hope that it will be useful,
 *    but WITHOUT ANY WARRANTY; without even the implied warranty of
 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *    GNU General Public License for more details.
 *
 *    You should have received a copy of the GNU General Public License along
 *    with this program; if not, write to the Free Software Foundation, Inc.,
 *    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 *    See LICENSE.exceptions in the root folder of this project for possible
 *    exceptions
 *
 */

#ifndef _CUBEFS_CORE_API_METADATA_TYPED_FIELD_HPP
#define _CUBEFS_CORE_API_METADATA_TYPED_FIELD_HPP

#include <cubefs/api/metadata/typed.hpp>
#include <cubefs/core/api/metadata/number.hpp>

#define CUBEFS_SOURCE "cubefs/core/api/metadata/typed_field.hpp:" CUBEFS_AT

namespace cubefs { namespace api {

using namespace cubefs::tools;

template <typename FieldType, typename Interface>
class TypedFieldImplT: public NumberFieldImplT<Interface> {
	typedef TypedFieldImplT<FieldType, Interface> 	Me;
	typedef NumberFieldImplT<Interface> 			Base;

    typedef Pair<Byte,      TypeCode<Base::BYTE> >            item1;
    typedef Pair<UByte,     TypeCode<Base::UBYTE> >           item2;
    typedef Pair<Short,     TypeCode<Base::SHORT> >           item3;
    typedef Pair<UShort,    TypeCode<Base::USHORT> >          item4;
    typedef Pair<Int,       TypeCode<Base::INT> >             item5;
    typedef Pair<UInt,      TypeCode<Base::UINT> >            item6;
    typedef Pair<BigInt,    TypeCode<Base::BIGINT> >          item7;

    typedef typename TypeListTool<
            item1,item2,item3,item4,item5,item6,
            item7>::List                   Type2CodeMap;

    static const Int kCode = Type2TypeMap<FieldType, Type2CodeMap, NullType >::Result::Value;

public:

    TypedFieldImplT(Int ptr, Int abi_ptr, StringRef name, Int offset, Int count):
        Base(ptr, abi_ptr, kCode, name, offset, sizeof(FieldType), count) {}

    TypedFieldImplT(Int ptr, Int abi_ptr, StringRef name, Int count):
        Base(ptr, abi_ptr, kCode, name, 0, sizeof(FieldType), count) {}

    TypedFieldImplT(Int ptr, Int abi_ptr, StringRef name):
        Base(ptr, abi_ptr, kCode, name, 0, sizeof(FieldType), 1) {}


    virtual const FieldType& GetValue(const void *mem, Int idx) const {
    	return (const FieldType&) *(const FieldType*) Base::ValuePtr(mem, idx);
    }

    virtual const FieldType &GetValueAbi(const void *mem, Int idx) const {
    	return (const FieldType&)* (const FieldType*) Base::ValuePtrAbi(mem, idx);
    }

    virtual const FieldType &GetValue(const void *mem) const {
    	return GetValue(mem, 0);
    }

    virtual const FieldType &GetValueAbi(const void *mem) const {
    	return GetValueAbi(mem, 0);
    }

    virtual void SetValue(void *mem, const FieldType& v, Int idx) {
    	FieldMetadataImplT<Interface>::SetValue(mem, &v, idx);
    }

    virtual void SetValueAbi(void *mem, const FieldType& v, Int idx) {
    	Base::SetValueAbi(mem, &v, idx);
    }

    virtual void SetValue(void *mem, const FieldType& v) {
    	SetValue(mem, v, 0);
    }

    virtual void SetValueAbi(void *mem, const FieldType& v) {
    	SetValueAbi(mem, v, 0);
    }

    virtual BigInt GetValue(Page* page, Int idx, bool abi) {
    	if (abi) {
    		return GetValueAbi(page->Ptr(), idx);
    	}
    	else {
    		return GetValue(page->Ptr(), idx);
    	}
    }

    virtual void SetValue(Page* page, Int idx, BigInt number, bool abi) {
    	if (abi) {
    		SetValueAbi(page->Ptr(), number, idx);
    	}
    	else {
    		SetValue(page->Ptr(), number, idx);
    	}
    }

	static void Init() {
	    AbstractType *type = new AbstractType(TypeNameFactory<Interface>::name(), NumberField::MyType());
	    type_ = type;

	    type->AddOperation(new BasicOperation<BigInt (Me::*)(Page*, Int, bool)>(type, "GetValue", "page", "idx", "abi", &Me::GetValue));
	    type->AddOperation(new BasicOperation<void (Me::*)(Page*, Int, BigInt, bool)>(type, "SetValue", "page", "idx", "number", "abi", &Me::SetValue));
	}

    CUBEFS_DECLARE_REFLECTION;
};

template <typename FieldType, typename Interface>
Type* TypedFieldImplT<FieldType, Interface>::type_ 					= NULL;

template <typename FieldType>
class TypedFieldImpl: public TypedFieldImplT<FieldType, TypedField<FieldType> > {
	typedef TypedFieldImplT<FieldType, TypedField<FieldType> > Base;
public:
	TypedFieldImpl(Int ptr, Int abi_ptr, StringRef name):
	        Base(ptr, abi_ptr, name) {}

	TypedFieldImpl(Int ptr, Int abi_ptr, StringRef name, Int count):
		        Base(ptr, abi_ptr, name, count) {}
};


}}

#undef CUBEFS_SOURCE
#endif
